package com.cmc.cloud.cmclink.doc.service.impl.multimodaltransport;

import cn.hutool.core.bean.BeanUtil;
import com.cmc.cloud.cmclink.agt.api.base.req.document.ContainerInfoDTO;
import com.cmc.cloud.cmclink.agt.api.base.req.document.DocumentAgreementRateInfoReqDTO;
import com.cmc.cloud.cmclink.agt.api.base.resp.document.AgreementRateInfoDTO;
import com.cmc.cloud.cmclink.agt.api.base.resp.document.DocumentAgreementRateInfoRespDTO;
import com.cmc.cloud.cmclink.agt.api.base.resp.document.RateInfoDTO;
import com.cmc.cloud.cmclink.agt.api.base.resp.document.rate.BargeVersionRateRespDocumentDTO;
import com.cmc.cloud.cmclink.agt.api.base.resp.document.rate.DeclareVersionRateRespDocumentDTO;
import com.cmc.cloud.cmclink.agt.api.base.resp.document.rate.TerminalVersionRateBargeRespDocumentDTO;
import com.cmc.cloud.cmclink.doc.api.document.emums.*;
import com.cmc.cloud.cmclink.doc.constants.CommonConstants;
import com.cmc.cloud.cmclink.doc.constants.errorconstant.BlnoLocalChargeErrorCodeConstants;
import com.cmc.cloud.cmclink.doc.constants.errorconstant.MultiLocalCostErrorCodeConstants;
import com.cmc.cloud.cmclink.doc.dto.AtuAndTerminalCodeDto;
import com.cmc.cloud.cmclink.doc.dto.MultiLclInfoDto;
import com.cmc.cloud.cmclink.doc.dto.MultiLocalCostTempDto;
import com.cmc.cloud.cmclink.doc.dto.VslvoyGroupByKeyDto;
import com.cmc.cloud.cmclink.doc.entity.*;
import com.cmc.cloud.cmclink.doc.mapper.*;
import com.cmc.cloud.cmclink.doc.rpcservice.AgreementService;
import com.cmc.cloud.cmclink.doc.rpcservice.CustomerRpcService;
import com.cmc.cloud.cmclink.doc.rpcservice.VoyScheduleService;
import com.cmc.cloud.cmclink.doc.service.BlnoCalculateOperateService;
import com.cmc.cloud.cmclink.doc.service.multimodaltransport.MultiLocalCostService;
import com.cmc.cloud.cmclink.doc.util.I18nsUtil;
import com.cmc.cloud.cmclink.doc.util.OogUtil;
import com.cmc.cloud.cmclink.doc.util.RedisUtils;
import com.cmc.cloud.cmclink.doc.vo.multimodaltransport.localcost.*;
import com.cmc.cloud.cmclink.doc.vo.sovo.sovslvoyvo.SoVslvoyScheduleVO;
import com.cmc.cloud.cmclink.svl.api.base.req.BargePolPodReqDTO;
import com.cmes.framework.common.exception.util.ServiceExceptionUtil;
import com.cmes.framework.common.pojo.CommonResult;
import com.cmes.framework.mybatis.core.query.LambdaQueryWrapperX;
import com.cmes.framework.security.core.util.SecurityFrameworkUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description: 多式联运
 * @Author: lzp
 * @Date: 2025/11/4 17:41
 */
@Service
@Slf4j
public class MultiLocalCostServiceImpl implements MultiLocalCostService {

    @Resource
    private CustomerRpcService customerRpcService;

    @Resource
    private BlnoMapper blnoMapper;

    @Resource
    private BlnoCntrMapper blnoCntrMapper;

    @Resource
    private MultiLocalCostMapper multiLocalCostMapper;

    @Resource
    private MultiLocalCostDetailMapper multiLocalCostDetailMapper;

    @Resource
    private CalculateOperateLogMapper calculateOperateLogMapper;

    @Resource
    private BlnoVslvoyMapper blnoVslvoyMapper;

    @Resource
    private VoyScheduleService voyScheduleService;

    @Resource
    private AgreementService agreementService;

    @Resource
    private BlnoCargoMapper blnoCargoMapper;

    @Resource
    private OogUtil oogUtil;

    @Resource
    private MultiOpMapper multiOpMapper;

    @Resource
    private BlnoCalculateOperateService blnoCalculateOperateService;

    @Resource
    private I18nsUtil i18nsUtil;

    @Resource
    private RedisUtils redisUtils;

    @Value("${multi.local.cost.redisTimeOut}")
    private Long redisTimeOut;

    private static final List<String> SI_STATUS = Arrays.asList(SiStatusEnum.BookingPendingSpace.getValue(), SiStatusEnum.AwaitingMaterialUploaded.getValue(), SiStatusEnum.BookingConfirmed.getValue(), SiStatusEnum.BookingRejected.getValue());

    private static final String DATA_TYPE = "ETU";
    private static final String BARGE = "barge";
    private static final String DECLARE = "declare";
    private static final String TERMINAL = "terminal";

    /**
     * 进口redis标记
     */
    private static final String MULTI_LOCAL_COST_IM = "multiLocalCost:im:";
    /**
     * 出口redis标记
     */
    private static final String MULTI_LOCAL_COST_EX = "multiLocalCost:ex:";

    private static final String SPLITFLAG = "|";

    @Override
    public List<MultiBlnoLocalCostRespVO> getBlnoInfoLocalCost(MultiBlnoLocalCostReqVO reqVo) {

        boolean check = StringUtils.isBlank(reqVo.getBlNo()) && (StringUtils.isBlank(reqVo.getLinerVslCode()) || StringUtils.isBlank(reqVo.getLinerVoyCode()));
        if (check) {
            throw ServiceExceptionUtil.exception(BlnoLocalChargeErrorCodeConstants.BLNO_LOCAL_CHARGE_IS_NOT_ALL_NULL);
        }
        List<String> rejectStatuList = new ArrayList<>();
        rejectStatuList.add(SiStatusEnum.BookingDraft.getValue());
        rejectStatuList.add(SiStatusEnum.BookingProcessing.getValue());
        reqVo.setBlNoStatus(rejectStatuList);
        List<MultiBlnoLocalCostRespVO> multiBlnoLocalCostRespVoList = new ArrayList<>(128);
        if (EmImTypeEnum.EXPORT.getValue().equals(reqVo.getImExType())) {
            multiBlnoLocalCostRespVoList = blnoMapper.getMultiExBlnoLocalCostInfo(reqVo);
        } else {
            multiBlnoLocalCostRespVoList = blnoMapper.getMultiImBlnoLocalCostInfo(reqVo);
        }
        if (CollectionUtils.isEmpty(multiBlnoLocalCostRespVoList)) {
            return Collections.emptyList();
        }
        List<Long> ids = multiBlnoLocalCostRespVoList.stream().map(MultiBlnoLocalCostRespVO::getId).collect(Collectors.toList());
        List<MultiBlnoCntrVO> multiBlnoCntrVoList = blnoCntrMapper.getblnoCntrInfo(ids);
        Map<Long, String> mapCntr = multiBlnoCntrVoList.stream().collect(Collectors.toMap(MultiBlnoCntrVO::getBlnoId, MultiBlnoCntrVO::getCntr));
        List<String> codes = multiBlnoLocalCostRespVoList.stream().map(MultiBlnoLocalCostRespVO::getBkpCode).collect(Collectors.toList());
        Map<String, String> companyNameMap = customerRpcService.getCompanyNameMap(codes);
        List<BlnoCargoDO> cargoDoList = getBlnoCargoInfo(ids);
        Map<Long, List<BlnoCargoDO>> cargoMap = cargoDoList.stream().collect(Collectors.groupingBy(BlnoCargoDO::getBlnoId));
        for (MultiBlnoLocalCostRespVO blnoLocalCostRespVo : multiBlnoLocalCostRespVoList) {

            if (cargoMap.containsKey(blnoLocalCostRespVo.getId())) {
                blnoLocalCostRespVo.setCargoType(cargoMap.get(blnoLocalCostRespVo.getId()).get(0).getCargoType());
            }
            if (StringUtils.isNotBlank(blnoLocalCostRespVo.getBkpCode())) {
                blnoLocalCostRespVo.setBkpName(companyNameMap.getOrDefault(blnoLocalCostRespVo.getBkpCode(), ""));
            }
            List<String> cntrList = Arrays.asList(mapCntr.get(blnoLocalCostRespVo.getId()).split(";"));
            List<String> cntrs = new ArrayList<>(16);
            for (String cntr : cntrList) {
                cntrs.add(blnoLocalCostRespVo.getCntrStatus() + "/" + cntr);
            }
            blnoLocalCostRespVo.setCntrs(cntrs);
        }
        return multiBlnoLocalCostRespVoList;
    }

    @Override
    public void createMultiLocalCost(MultiLocalCostCreateReqVO createReqVO) {
        // 插入
        MultiLocalCostDO multiLocalCost = BeanUtil.copyProperties(createReqVO, MultiLocalCostDO.class);
        multiLocalCost.setRecordType(RecordTypeEnum.MANUAL.getValue());
        multiLocalCost.setToAccount(false);
        multiLocalCost.setRedStatus(RedStatusEnum.NOTRED.getValue());
        multiLocalCost.setOriginalId(null);
        multiLocalCostMapper.insert(multiLocalCost);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void updateMultiLocalCost(MultiLocalCostUpdateReqVO updateReqVo) {
        // 校验存在
        validateMultiLocalCostExists(updateReqVo.getId(), true);
        // 更新
        MultiLocalCostDO updateObj = BeanUtil.copyProperties(updateReqVo, MultiLocalCostDO.class);
        updateObj.setUpdater(String.valueOf(SecurityFrameworkUtils.getLoginUserId()));
        updateObj.setUpdateTime(LocalDateTime.now());
        multiLocalCostMapper.updateById(updateObj);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void deleteMultiLocalCost(Long id) {
        // 校验存在
        validateMultiLocalCostExists(id, false);
        // 删除
        multiLocalCostMapper.deleteById(id);
    }

    private void validateMultiLocalCostExists(Long id, Boolean flag) {
        MultiLocalCostDO multiLocalCostDo = multiLocalCostMapper.selectById(id);
        if (multiLocalCostDo == null) {
            throw ServiceExceptionUtil.exception(MultiLocalCostErrorCodeConstants.MULTI_LOCAL_COST_NOT_EXISTS);
        }
        if (flag && StringUtils.equals(multiLocalCostDo.getRecordType(), RecordTypeEnum.MANUAL.getValue())) {
            throw ServiceExceptionUtil.exception(MultiLocalCostErrorCodeConstants.MULTI_LOCAL_COST_NOT_ALLOW_MODIFY);
        }
    }

    @Override
    public List<MultiLocalCostRespVO> getMultiLocalCost(Long blnoId, String imExType) {
        LambdaQueryWrapperX<MultiLocalCostDO> lambdaQueryWrapperx = new LambdaQueryWrapperX<>();
        lambdaQueryWrapperx.eqIfPresent(MultiLocalCostDO::getBlnoId, blnoId).eq(MultiLocalCostDO::getImExType, imExType).orderByDesc(MultiLocalCostDO::getChargeCode).orderByDesc(MultiLocalCostDO::getRedStatus);
        List<MultiLocalCostDO> blnoLocalCostDoList = multiLocalCostMapper.selectList(lambdaQueryWrapperx);
        if (CollectionUtils.isEmpty(blnoLocalCostDoList)) {
            return Collections.emptyList();
        }
        List<String> codes = blnoLocalCostDoList.stream().map(MultiLocalCostDO::getSupplier).collect(Collectors.toList());
        Map<String, String> companyNameMap = customerRpcService.getCompanyNameMap(codes);
        List<MultiLocalCostRespVO> multiLocalCostRespVoList = BeanUtil.copyToList(blnoLocalCostDoList, MultiLocalCostRespVO.class);
        for (MultiLocalCostRespVO multiLocalCostRespVo : multiLocalCostRespVoList) {
            if (StringUtils.isNotBlank(multiLocalCostRespVo.getSupplier())) {
                multiLocalCostRespVo.setSupplierName(companyNameMap.getOrDefault(multiLocalCostRespVo.getSupplier(), ""));
            }

        }
        return multiLocalCostRespVoList;
    }

    @Override
    public List<MultiLocalCostDetailRespVO> getMultiLocalCostDetail(Long blnoId, String imExType) {
        LambdaQueryWrapperX<MultiLocalCostDetailDO> lambdaQueryWrapperX = new LambdaQueryWrapperX<>();
        lambdaQueryWrapperX.eq(MultiLocalCostDetailDO::getBlnoId, blnoId).eq(MultiLocalCostDetailDO::getImExType, imExType).orderByDesc(MultiLocalCostDetailDO::getChargeCode);
        List<MultiLocalCostDetailDO> multiLocalCostDetailDoList = multiLocalCostDetailMapper.selectList(lambdaQueryWrapperX);
        if (CollectionUtils.isEmpty(multiLocalCostDetailDoList)) {
            return Collections.emptyList();
        }
        List<String> codes = multiLocalCostDetailDoList.stream().map(MultiLocalCostDetailDO::getSupplier).collect(Collectors.toList());
        Map<String, String> companyNameMap = customerRpcService.getCompanyNameMap(codes);
        List<MultiLocalCostDetailRespVO> multiLocalCostDetailRespVoList = BeanUtil.copyToList(multiLocalCostDetailDoList, MultiLocalCostDetailRespVO.class);
        for (MultiLocalCostDetailRespVO multiLocalCostRespVo : multiLocalCostDetailRespVoList) {
            if (StringUtils.isNotBlank(multiLocalCostRespVo.getSupplier())) {
                multiLocalCostRespVo.setSupplierName(companyNameMap.getOrDefault(multiLocalCostRespVo.getSupplier(), ""));
            }
        }
        return multiLocalCostDetailRespVoList;
    }

    @Override
    public CalculateOperateLogDO getMultiCalculateLogInfo(String blNo, String imExType) {
        String calculateType = null;
        if (ExImTypeEnum.EX.getValue().equals(imExType)) {
            calculateType = CalculateTypeEnum.MULTI_CALCULATE_LOCAL_COST_EX.getValue();
        } else {
            calculateType = CalculateTypeEnum.MULTI_CALCULATE_LOCAL_COST_IM.getValue();
        }
        LambdaQueryWrapperX<CalculateOperateLogDO> lambdaQueryWrapperX = new LambdaQueryWrapperX<>();
        lambdaQueryWrapperX.eqIfPresent(CalculateOperateLogDO::getBusinessNo, blNo).eqIfPresent(CalculateOperateLogDO::getCalculateType, calculateType).orderByDesc(CalculateOperateLogDO::getCreateTime).last(" limit 1");

        List<CalculateOperateLogDO> calculateOperateLogDoList = calculateOperateLogMapper.selectList(lambdaQueryWrapperX);
        if (CollectionUtils.isEmpty(calculateOperateLogDoList)) {
            return new CalculateOperateLogDO();
        }
        return calculateOperateLogDoList.get(0);
    }

    @Override
    public List<BlnoCntrDO> getBlNoCntrInfo(Long blId) {
        LambdaQueryWrapperX<BlnoCntrDO> blnoCntrDoLambdaQueryWrapperX = new LambdaQueryWrapperX<>();
        blnoCntrDoLambdaQueryWrapperX.eqIfPresent(BlnoCntrDO::getBlnoId, blId);
        List<BlnoCntrDO> blnoCntrDoList = blnoCntrMapper.selectList(blnoCntrDoLambdaQueryWrapperX);
        if (CollectionUtils.isEmpty(blnoCntrDoList)) {
            return Collections.emptyList();
        }
        return blnoCntrDoList;
    }

    /**
     * 多联成本计算
     * 1、
     *
     * @param multiBlnoCalcutlatReqVo
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public List<MultiBlnoLocalCostResultVO> multiCalcutlate(MultiBlnoCalcutlatReqVO multiBlnoCalcutlatReqVo) {
        List<MultiLocalCostDetailDO> multiLocalCostDetailDoList = new ArrayList<>(256);
        List<MultiBlnoLocalCostResultVO> calcuteResult = new ArrayList<>(128);
        //查询提单信息
        List<BlnoDO> blnoDoList = getBlnoInfo(multiBlnoCalcutlatReqVo.getIds());
        if (CollectionUtils.isEmpty(blnoDoList)) {
            throw ServiceExceptionUtil.exception(MultiLocalCostErrorCodeConstants.MULTI_LOCAL_COST_UNVALID_BLNO);
        }
        //reids 加锁
        List<Long> lockIds = new ArrayList<>(128);
        multiLocalCostAddRedis(blnoDoList, multiBlnoCalcutlatReqVo.getImExType(), lockIds, calcuteResult, multiBlnoCalcutlatReqVo.getType());
        if (CollectionUtils.isEmpty(blnoDoList)) {
            this.multiDeleteRedis(lockIds, multiBlnoCalcutlatReqVo.getImExType());
            throw ServiceExceptionUtil.exception(MultiLocalCostErrorCodeConstants.MULTI_LOCAL_COST_ALL_CALCUTE);
        }
        List<Long> ids = blnoDoList.stream().map(BlnoDO::getId).collect(Collectors.toList());
        //查询箱信息
        List<BlnoCntrDO> blnoCntrDos = getBlnoCntrInfo(ids);
        Map<Long, List<BlnoCntrDO>> cntrMap = blnoCntrDos.stream().collect(Collectors.groupingBy(BlnoCntrDO::getBlnoId));
        //查询货物信息
        List<BlnoCargoDO> blnoCargoDos = getBlnoCargoInfo(ids);
        Map<Long, List<BlnoCargoDO>> cargoMap = blnoCargoDos.stream().collect(Collectors.groupingBy(BlnoCargoDO::getBlnoId));
        //查询barge船名航次信息进口取最后一段出口取第一段的驳船信息
        List<BlnoVslvoyDO> vslvoyList = getBlnoVslvoyInfo(ids, multiBlnoCalcutlatReqVo.getImExType());
        Map<Long, List<BlnoVslvoyDO>> mapVslvoy = vslvoyList.stream().collect(Collectors.groupingBy(BlnoVslvoyDO::getBlnoId));
        //获取协议的费率以及以及将请求参数保存日志
        ArrayList<DocumentAgreementRateInfoRespDTO> agreementRate = dealMultiRateData(multiBlnoCalcutlatReqVo.getType(), blnoDoList, cntrMap, ids, multiBlnoCalcutlatReqVo.getImExType(), cargoMap, vslvoyList);
        if (CollectionUtils.isEmpty(agreementRate)) {
            this.multiDeleteRedis(ids, multiBlnoCalcutlatReqVo.getImExType());
            throw ServiceExceptionUtil.exception(MultiLocalCostErrorCodeConstants.MULTI_LOCAL_COST_AGRGREMENT_ERROR);
        }
        Map<String, List<DocumentAgreementRateInfoRespDTO>> agreementResult = agreementRate.stream().collect(Collectors.groupingBy(DocumentAgreementRateInfoRespDTO::getBlNo));
        //计算明细
        multiLocalCostDetailDoList = dealCalcutlate(blnoDoList, agreementResult, cargoMap, cntrMap, calcuteResult, mapVslvoy, multiBlnoCalcutlatReqVo.getType(), multiBlnoCalcutlatReqVo.getImExType());
        //删除本次所有的参与计算的详情表数据
        this.deleteMultiLocalCostDetailInfo(ids, multiBlnoCalcutlatReqVo.getType());
        //处理拼箱
        this.dealLclInfo(multiLocalCostDetailDoList, blnoCntrDos);
        if (CollectionUtils.isEmpty(multiLocalCostDetailDoList)) {
            this.multiDeleteRedis(ids, multiBlnoCalcutlatReqVo.getImExType());
            throw ServiceExceptionUtil.exception(MultiLocalCostErrorCodeConstants.MULTI_LOCAL_COST_ALL_LCL_CALCUTE);
        }
        //从明细汇总到主数据
        List<MultiLocalCostDO> multiLocalCostDo = summaryMultiLocalCostDo(multiLocalCostDetailDoList);
        //处理红冲和保存
        redImpact(multiLocalCostDo, multiLocalCostDetailDoList, multiBlnoCalcutlatReqVo.getType(), multiBlnoCalcutlatReqVo.getImExType());
        //redis解锁
        this.multiDeleteRedis(ids, multiBlnoCalcutlatReqVo.getImExType());
        return Collections.emptyList();
    }

    /**
     * 1.逻辑删除所有的明细
     * 2.将所有新计算的明细数据重新插入到数据库
     * 3.查出所有的已计算过的提单的费用数据不含手动添加的
     * 4.计算过提单数据包括红冲+被红冲的数据，已报财务数据，未报财务数据
     * 4.1红冲+被红冲的数据无需做处理
     * 4.2已报财务的数据分为以下几种情况
     * 4.2.1已报财务重新计算的结果中没有这条记录需要红冲
     * 4.2.2已报财务重新计算的结果相同无需处理
     * 4.2.3已报财务重新计算的结果中存在这条数据且金额不一致需要红冲
     * 4.3未报财务数据逻辑删除然后重新插入
     * 5.本次计算新增的记录直接插入
     * 6.存在不存在的维度是charge_code,per、cntr_type、cntr_size、cargo_type、rate、currency、local_port、ImExType、calcyteType
     * 7.红冲条件：payer和rate两个有一个发生变化都要红冲
     * 8.手动添加的记录不做处理
     *
     * @param newMultiLocalCostDoList
     * @param newMultiLocalCostDetailDoList
     */
    public void redImpact(List<MultiLocalCostDO> newMultiLocalCostDoList, List<MultiLocalCostDetailDO> newMultiLocalCostDetailDoList, String calcuteType, String imExType) {
        List<Long> costBlIds = newMultiLocalCostDoList.stream().map(MultiLocalCostDO::getBlnoId).collect(Collectors.toList());
        //查询出所有被计算过的提单的历史计算结果
        LambdaQueryWrapperX<MultiLocalCostDO> multiLocalCostLambdaQueryWrapper = new LambdaQueryWrapperX<>();
        multiLocalCostLambdaQueryWrapper.inIfPresent(MultiLocalCostDO::getBlnoId, costBlIds)
                .eq(MultiLocalCostDO::getRecordType, RecordTypeEnum.AUTO.getValue())
                .eq(MultiLocalCostDO::getImExType, imExType);
        MultiLocalCostCalculationEnum multiLocalCostCalculationEnum = MultiLocalCostCalculationEnum.valueOf(calcuteType);
        switch (multiLocalCostCalculationEnum) {
            //报关费
            case CUSTOMSCLEARANCEFEE:
                //支线费
            case FEEDERCHARGE:
                //装卸费
            case LOADINGUNLOADINGCHARGES:
                multiLocalCostLambdaQueryWrapper.eq(MultiLocalCostDO::getCalcuteType, calcuteType);
                break;
            default:
                break;
        }
        List<MultiLocalCostDO> oldLocalChargeDoList = multiLocalCostMapper.selectList(multiLocalCostLambdaQueryWrapper);

        if (CollectionUtils.isNotEmpty(oldLocalChargeDoList)) {
            //所有红冲的数据
            List<MultiLocalCostDO> oldRedAllRedLocalCost = oldLocalChargeDoList.stream().filter(item -> RedStatusEnum.RED.getValue().equals(item.getRedStatus())).collect(Collectors.toList());
            //产生的红冲的记录数据
            List<MultiLocalCostDO> newRedAllLocalCost = new ArrayList<>(128);
            //需要更新为红冲标记的数据记录
            List<MultiLocalCostDO> redBlnoInfo = new ArrayList<>(128);
            //从历史计算结果中将红冲数据剔除掉
            if (CollectionUtils.isNotEmpty(oldRedAllRedLocalCost)) {
                oldLocalChargeDoList.removeAll(oldRedAllRedLocalCost);
            }
            //过滤出已经报财务的历史数据
            //已报财务历史计算的数据
            List<MultiLocalCostDO> oldToAccountLocalCost = oldLocalChargeDoList.stream().filter(MultiLocalCostDO::getToAccount).collect(Collectors.toList());
            //未报财务历史计算的数据
            List<MultiLocalCostDO> oldNotToAccountLocalCost = oldLocalChargeDoList.stream().filter(item -> !item.getToAccount()).collect(Collectors.toList());
            //处理已经报财务的情况，报财务不存在，报财务存在且金额一致，报财务存在金额不一致
            if (CollectionUtils.isNotEmpty(oldToAccountLocalCost)) {
                Map<Long, List<MultiLocalCostDO>> newLocalChargeMap = newMultiLocalCostDoList.stream().collect(Collectors.groupingBy(MultiLocalCostDO::getBlnoId));
                for (MultiLocalCostDO oldToAccount : oldToAccountLocalCost) {
                    //newBlIdLocalChargeList 不会为空无需判断空
                    List<MultiLocalCostDO> newBlIdLocalChargeList = newLocalChargeMap.get(oldToAccount.getBlnoId());
                    MultiLocalCostDO newLocalChargeExist = new MultiLocalCostDO();
                    boolean flag = false;
                    for (MultiLocalCostDO newLocalCharge : newBlIdLocalChargeList) {
                        //判断已经报财务的是否存在,flag = true时存在,false说明不存在需要红冲
                        flag = this.checkLocalCost(oldToAccount, newLocalCharge);
                        if (flag) {
                            newLocalChargeExist = newLocalCharge;
                            break;
                        }
                    }
                    if (flag) {
                        //存在判断金额和payer是否一致，
                        if (newLocalChargeExist.getSupplier().equals(oldToAccount.getSupplier()) && newLocalChargeExist.getAmount().compareTo(oldToAccount.getAmount()) == 0) {
                            newMultiLocalCostDoList.remove(newLocalChargeExist);
                        } else {
                            newRedAllLocalCost.add(this.redLocalCost(oldToAccount));
                            redBlnoInfo.add(oldToAccount);
                        }
                    } else {
                        //不存在直接红冲
                        newRedAllLocalCost.add(this.redLocalCost(oldToAccount));
                        redBlnoInfo.add(oldToAccount);
                    }
                }
            }
            //删除所有未报财务的数据
            if (CollectionUtils.isNotEmpty(oldNotToAccountLocalCost)) {
                List<Long> deleteIds = oldNotToAccountLocalCost.stream().map(MultiLocalCostDO::getId).collect(Collectors.toList());
                LambdaQueryWrapperX<MultiLocalCostDO> costDoLambdaQueryWrapperX = new LambdaQueryWrapperX<>();
                costDoLambdaQueryWrapperX.inIfPresent(MultiLocalCostDO::getId, deleteIds);
                multiLocalCostMapper.delete(costDoLambdaQueryWrapperX);
            }
            //更新被红冲记录的标识
            if (CollectionUtils.isNotEmpty(redBlnoInfo)) {
                for (MultiLocalCostDO redBlno : redBlnoInfo) {
                    redBlno.setRedStatus(RedStatusEnum.RED.getValue());
                    redBlno.setUpdater(String.valueOf(Objects.requireNonNull(SecurityFrameworkUtils.getLoginUser()).getId()));
                    redBlno.setUpdateTime(LocalDateTime.now());
                }
                multiLocalCostMapper.updateBatch(redBlnoInfo);
            }
            //将所有红冲的记录插入到新计算的结果中
            if (CollectionUtils.isNotEmpty(newRedAllLocalCost)) {
                newMultiLocalCostDoList.addAll(newRedAllLocalCost);
            }
        }
        multiLocalCostDetailMapper.insertBatch(newMultiLocalCostDetailDoList);
        if (CollectionUtils.isNotEmpty(newMultiLocalCostDoList)) {
            multiLocalCostMapper.insertBatch(newMultiLocalCostDoList);
        }
    }


    /**
     * 红冲数据
     *
     * @param oldToAccount 红冲数据
     * @return BlnoLocalChargeDO
     */
    private MultiLocalCostDO redLocalCost(MultiLocalCostDO oldToAccount) {
        return MultiLocalCostDO.builder()
                .blnoId(oldToAccount.getBlnoId())
                .imExType(oldToAccount.getImExType())
                .chargeCode(oldToAccount.getChargeCode())
                .per(oldToAccount.getPer())
                .cntrType(oldToAccount.getCntrType())
                .cntrSize(oldToAccount.getCntrSize())
                .cargoType(oldToAccount.getCargoType())
                .quantity(oldToAccount.getQuantity())
                .rate(oldToAccount.getRate())
                .amount(oldToAccount.getAmount().negate())
                .supplier(oldToAccount.getSupplier())
                .currency(oldToAccount.getCurrency())
                .toAccount(false)
                .recordType(RecordTypeEnum.AUTO.getValue())
                .localPort(oldToAccount.getLocalPort())
                .redStatus(RedStatusEnum.RED.getValue())
                .originalId(oldToAccount.getId())
                .calcuteType(oldToAccount.getCalcuteType()).build();
    }

    /**
     * 判断是否存在 charge_code,per、cntr_type、cntr_size、cargo_type、currency、local_port、ImExType,calcuteType存在一致返回true,否则返回false
     *
     * @param oldLocalCost 数据库中的计算数据
     * @param newLocalCost 新计算生成的数据
     * @return Boolean
     */
    private Boolean checkLocalCost(MultiLocalCostDO oldLocalCost, MultiLocalCostDO newLocalCost) {

        if (!oldLocalCost.getChargeCode().equals(newLocalCost.getChargeCode())) {
            return false;
        }
        if (!oldLocalCost.getPer().equals(newLocalCost.getPer())) {
            return false;
        }
        if (!oldLocalCost.getCntrType().equals(newLocalCost.getCntrType())) {
            return false;
        }
        if (!oldLocalCost.getCntrSize().equals(newLocalCost.getCntrSize())) {
            return false;
        }
        if (!oldLocalCost.getCargoType().equals(newLocalCost.getCargoType())) {
            return false;
        }
        if (!oldLocalCost.getCurrency().equals(newLocalCost.getCurrency())) {
            return false;
        }
        if (!oldLocalCost.getLocalPort().equals(newLocalCost.getLocalPort())) {
            return false;
        }
        if (!oldLocalCost.getCalcuteType().equals(newLocalCost.getCalcuteType())) {
            return false;
        }
        return oldLocalCost.getImExType().equals(newLocalCost.getImExType());
    }

    /**
     * 按照charge_code,per、cntr_type、cntr_size、cargo_type、rate、currency、payer、local_port、Payment、ImExType、blid、Supplier维度进行分类汇总的
     *
     * @param multiLocalCostDetailDos
     * @return
     */
    private List<MultiLocalCostDO> summaryMultiLocalCostDo(List<MultiLocalCostDetailDO> multiLocalCostDetailDos) {
        List<MultiLocalCostDO> blnoLocalCostDoList = new ArrayList<>(64);
        Map<MultiLocalCostTempDto, List<MultiLocalCostDetailDO>> multiLocalCostTempVoListMap = multiLocalCostDetailDos.stream().collect(Collectors.groupingBy(item ->
                new MultiLocalCostTempDto(item.getBlnoId(),
                        item.getChargeCode(),
                        item.getPer(),
                        item.getCntrType(),
                        item.getCntrSize(),
                        item.getCargoType(),
                        item.getRate(),
                        item.getCurrency(),
                        item.getSupplier(),
                        item.getLocalPort(),
                        item.getImExType(),
                        item.getPayment(),
                        item.getCalcuteType())
        ));
        for (Map.Entry<MultiLocalCostTempDto, List<MultiLocalCostDetailDO>> entry : multiLocalCostTempVoListMap.entrySet()) {
            MultiLocalCostDO blnoLocalCostDo = new MultiLocalCostDO();
            blnoLocalCostDo = BeanUtil.copyProperties(entry.getKey(), MultiLocalCostDO.class);
            blnoLocalCostDo.setRecordType(RecordTypeEnum.AUTO.getValue());
            blnoLocalCostDo.setRedStatus(RedStatusEnum.NOTRED.getValue());
            blnoLocalCostDo.setToAccount(false);
            blnoLocalCostDo.setQuantity(entry.getValue().size());
            //汇总金额
            BigDecimal amount = entry.getValue().stream().map(item -> Optional.ofNullable(item.getAmount()).orElse(BigDecimal.ZERO))
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            blnoLocalCostDo.setAmount(amount);
            blnoLocalCostDoList.add(blnoLocalCostDo);
        }
        return blnoLocalCostDoList;
    }


    /**
     * 处理拼箱
     * 1、过滤出所有的拼箱数据
     * 2、找出所有拼箱数据所对应的驳船的船名航次
     * 3、查询拼箱的数据是否已经计算过，如果计算过在本次计算的结果中删除掉
     * 4、对已剔除掉已经计算过的拼箱数后的数据进行去重处理。去重规则是船名航次+箱号+费用类型，如果有重复的数据保留blnoid最小的记录
     *
     * @param multiLocalCostDetailDoList
     * @param blnoCntrDos
     * @return
     */
    private void dealLclInfo(List<MultiLocalCostDetailDO> multiLocalCostDetailDoList, List<BlnoCntrDO> blnoCntrDos) {
        //过滤出所有的拼箱数据
        List<BlnoCntrDO> blnoLclCntrDoList = blnoCntrDos.stream().filter(item -> item.getLcl()).collect(Collectors.toList());
        if (CollectionUtils.isEmpty(blnoLclCntrDoList)) {
            return;
        }
        //根据拼箱数据获取对应的驳船的船名航次数据信息
        List<MultiLclInfoDto> multiLclInfoDtos = new ArrayList<>(32);
        //组装拼箱的数据信息船名航次+箱号
        for (BlnoCntrDO blnoCntrDo : blnoLclCntrDoList) {
            for (MultiLocalCostDetailDO calcuteResult : multiLocalCostDetailDoList) {
                boolean flag = Objects.equals(blnoCntrDo.getBlnoId(), calcuteResult.getBlnoId()) && StringUtils.equals(blnoCntrDo.getCntrNo(), calcuteResult.getContainerNo());
                if (flag) {
                    MultiLclInfoDto multiLclInfoDto = new MultiLclInfoDto();
                    multiLclInfoDto.setCntrNo(blnoCntrDo.getCntrNo());
                    multiLclInfoDto.setVesselCode(calcuteResult.getVesselCode());
                    multiLclInfoDto.setVoyageCode(calcuteResult.getVoyageCode());
                    multiLclInfoDto.setCalcuteType(calcuteResult.getCalcuteType());
                    multiLclInfoDtos.add(multiLclInfoDto);
                }
            }
        }
        if (CollectionUtils.isEmpty(multiLclInfoDtos)) {
            return;
        }
        //判断拼箱数据是否已经计算过
        List<String> vsls = multiLclInfoDtos.stream().map(MultiLclInfoDto::getVesselCode).collect(Collectors.toList());
        List<String> voys = multiLclInfoDtos.stream().map(MultiLclInfoDto::getVoyageCode).collect(Collectors.toList());
        List<String> cntrs = multiLclInfoDtos.stream().map(MultiLclInfoDto::getCntrNo).collect(Collectors.toList());
        List<String> calcuteTypes = multiLclInfoDtos.stream().map(MultiLclInfoDto::getCalcuteType).collect(Collectors.toList());
        LambdaQueryWrapperX<MultiLocalCostDetailDO> lambdaQueryWrapper = new LambdaQueryWrapperX<>();
        lambdaQueryWrapper.in(MultiLocalCostDetailDO::getVesselCode, vsls)
                .in(MultiLocalCostDetailDO::getVoyageCode, voys)
                .in(MultiLocalCostDetailDO::getContainerNo, cntrs)
                .in(MultiLocalCostDetailDO::getCalcuteType, calcuteTypes);
        List<MultiLocalCostDetailDO> lclMultiLocalCostDetail = multiLocalCostDetailMapper.selectList(lambdaQueryWrapper);
        if (!CollectionUtils.isEmpty(lclMultiLocalCostDetail)) {
            List<MultiLocalCostDetailDO> alreadyCalcute = new ArrayList<>(32);
            for (MultiLocalCostDetailDO multilcl : lclMultiLocalCostDetail) {
                for (MultiLocalCostDetailDO calcute : multiLocalCostDetailDoList) {
                    boolean flag = StringUtils.equals(multilcl.getVoyageCode(), calcute.getVoyageCode()) &&
                            StringUtils.equals(multilcl.getVesselCode(), calcute.getVesselCode()) &&
                            StringUtils.equals(multilcl.getContainerNo(), calcute.getContainerNo()) &&
                            StringUtils.equals(multilcl.getCalcuteType(), calcute.getCalcuteType());
                    if (flag) {
                        alreadyCalcute.add(calcute);
                    }
                }
            }
            //删除已经计算过的数据
            if (CollectionUtils.isNotEmpty(alreadyCalcute)) {
                multiLocalCostDetailDoList.removeAll(alreadyCalcute);
            }
        }
        //处理本次计算中按照船名航次重复的数据保留最小的数据
        if (CollectionUtils.isNotEmpty(multiLocalCostDetailDoList)) {
            Map<String, List<MultiLocalCostDetailDO>> repeatInfoMap = multiLocalCostDetailDoList.stream().filter(
                    item -> StringUtils.equals(item.getPer(), ChargeUnitCodeEnum.UNIT.getDocValue())).collect(Collectors.groupingBy(
                    item -> item.getVesselCode() + SPLITFLAG + item.getVoyageCode() + SPLITFLAG + item.getContainerNo() + SPLITFLAG + item.getCalcuteType()));
            List<MultiLocalCostDetailDO> result = new ArrayList<>();
            if (repeatInfoMap.isEmpty()) {
                return;
            }
            for (List<MultiLocalCostDetailDO> group : repeatInfoMap.values()) {
                if (group.size() > 1) { // 重复分组
                    // 求BlnoId最大的元素（返回Optional）
                    Optional<MultiLocalCostDetailDO> minOpt = group.stream()
                            .max((d1, d2) -> Long.compare(d1.getBlnoId(), d2.getBlnoId()));
                    MultiLocalCostDetailDO minObj = minOpt.get();
                    result.add(minObj);
                }
            }
            if (CollectionUtils.isNotEmpty(result)) {
                multiLocalCostDetailDoList.removeAll(result);
            }
        }
    }


    private void multiDeleteRedis(List<Long> calcuteIds, String imExType) {
        if (ExImTypeEnum.EX.getValue().equals(imExType)) {
            for (Long blnoId : calcuteIds) {
                redisUtils.deleteString(MULTI_LOCAL_COST_EX + blnoId);
            }
        } else {
            for (Long blnoId : calcuteIds) {
                redisUtils.deleteString(MULTI_LOCAL_COST_IM + blnoId);
            }
        }

    }

    private void multiLocalCostAddRedis(List<BlnoDO> blnoDoList, String imExType, List<Long> lockIds, List<MultiBlnoLocalCostResultVO> calcuteResult, String calcutype) {
        Locale currentLocale = LocaleContextHolder.getLocale();
        List<BlnoDO> lockBlnos = new ArrayList<>(16);
        if (ExImTypeEnum.EX.getValue().equals(imExType)) {
            for (BlnoDO blnoDO : blnoDoList) {
                if (redisUtils.checKeyExist(MULTI_LOCAL_COST_EX + blnoDO.getId())) {
                    MultiBlnoLocalCostResultVO multiBlnoLocalCostResultVo = MultiBlnoLocalCostResultVO.builder()
                            .blno(blnoDO.getBlNo())
                            .type(calcutype)
                            .result(i18nsUtil.getMessage(MultiLocalCostErrorCodeConstants.MULTI_LOCAL_COST_CALCUTE.getCode(), currentLocale)).build();
                    calcuteResult.add(multiBlnoLocalCostResultVo);
                    lockIds.add(blnoDO.getId());
                    lockBlnos.add(blnoDO);
                } else {
                    redisUtils.setStringTimeOutByMinute(MULTI_LOCAL_COST_EX + blnoDO.getId(), String.valueOf(blnoDO.getId()), redisTimeOut);
                }
            }
        } else {
            for (BlnoDO blnoDO : blnoDoList) {
                if (redisUtils.checKeyExist(MULTI_LOCAL_COST_IM + blnoDO.getId())) {
                    MultiBlnoLocalCostResultVO multiBlnoLocalCostResultVo = MultiBlnoLocalCostResultVO.builder()
                            .blno(blnoDO.getBlNo())
                            .type(calcutype)
                            .result(i18nsUtil.getMessage(MultiLocalCostErrorCodeConstants.MULTI_LOCAL_COST_CALCUTE.getCode(), currentLocale)).build();
                    calcuteResult.add(multiBlnoLocalCostResultVo);
                    lockIds.add(blnoDO.getId());
                    lockBlnos.add(blnoDO);
                } else {
                    redisUtils.setStringTimeOutByMinute(MULTI_LOCAL_COST_IM + blnoDO.getId(), String.valueOf(blnoDO.getId()), redisTimeOut);
                }
            }
        }
        if (CollectionUtils.isNotEmpty(lockBlnos)) {
            blnoDoList.removeAll(lockBlnos);
        }
    }

    /**
     * 处理计算逻辑
     *
     * @param blnoDoList
     * @param agreementResult
     * @param cargoMap
     * @param cntrMap
     * @param calcuteResult
     * @param mapVslvoy
     * @param calcuteType
     * @param imExType
     * @return
     */
    public List<MultiLocalCostDetailDO> dealCalcutlate(List<BlnoDO> blnoDoList,
                                                       Map<String, List<DocumentAgreementRateInfoRespDTO>> agreementResult,
                                                       Map<Long, List<BlnoCargoDO>> cargoMap,
                                                       Map<Long, List<BlnoCntrDO>> cntrMap,
                                                       List<MultiBlnoLocalCostResultVO> calcuteResult,
                                                       Map<Long, List<BlnoVslvoyDO>> mapVslvoy,
                                                       String calcuteType,
                                                       String imExType) {

        List<MultiLocalCostDetailDO> multiLocalCostDetailDoList = new ArrayList<>(256);
        MultiLocalCostCalculationEnum multiLocalCostCalculationEnum = MultiLocalCostCalculationEnum.valueOf(calcuteType);
        switch (multiLocalCostCalculationEnum) {
            //报关费
            case CUSTOMSCLEARANCEFEE:
                //支线费
            case FEEDERCHARGE:
                //装卸费
            case LOADINGUNLOADINGCHARGES:
                multiLocalCostDetailDoList.addAll(calcutlate(blnoDoList, agreementResult, cargoMap, cntrMap, calcuteResult, imExType, mapVslvoy, calcuteType));
                break;
            default:
                multiLocalCostDetailDoList.addAll(calcutlate(blnoDoList, agreementResult, cargoMap, cntrMap, calcuteResult, imExType, mapVslvoy, MultiLocalCostCalculationEnum.CUSTOMSCLEARANCEFEE.getValue()));
                multiLocalCostDetailDoList.addAll(calcutlate(blnoDoList, agreementResult, cargoMap, cntrMap, calcuteResult, imExType, mapVslvoy, MultiLocalCostCalculationEnum.FEEDERCHARGE.getValue()));
                multiLocalCostDetailDoList.addAll(calcutlate(blnoDoList, agreementResult, cargoMap, cntrMap, calcuteResult, imExType, mapVslvoy, MultiLocalCostCalculationEnum.LOADINGUNLOADINGCHARGES.getValue()));
                break;
        }
        return multiLocalCostDetailDoList;
    }

    /**
     * 报关费用计算
     *
     * @param blnoDoList      提单信息
     * @param agreementResult 协议信息
     * @param cargoMap        货物信息
     * @param cntrMap         箱信息
     * @param calcuteResult   计算结果
     * @param imExtype        进出口
     * @param mapVslvoy       船名航次
     * @param calcuteType     计算类型 报关费、直线费、装卸费
     * @return
     */
    public List<MultiLocalCostDetailDO> calcutlate(List<BlnoDO> blnoDoList,
                                                   Map<String, List<DocumentAgreementRateInfoRespDTO>> agreementResult,
                                                   Map<Long, List<BlnoCargoDO>> cargoMap,
                                                   Map<Long, List<BlnoCntrDO>> cntrMap,
                                                   List<MultiBlnoLocalCostResultVO> calcuteResult,
                                                   String imExtype,
                                                   Map<Long, List<BlnoVslvoyDO>> mapVslvoy,
                                                   String calcuteType) {
        List<MultiLocalCostDetailDO> multiLocalCostDetailDoList = new ArrayList<>(256);
        for (BlnoDO blnoDo : blnoDoList) {
            if (!cntrMap.containsKey(blnoDo.getId())) {
                MultiBlnoLocalCostResultVO multiBlnoLocalCostResultVo = MultiBlnoLocalCostResultVO.builder()
                        .result(i18nsUtil.getMessage(MultiLocalCostErrorCodeConstants.MULTI_LOCAL_COST_BLNO_CNTR_NOTEXIST.getCode(), LocaleContextHolder.getLocale()))
                        .type(calcuteType)
                        .blno(blnoDo.getBlNo()).build();
                calcuteResult.add(multiBlnoLocalCostResultVo);
                continue;
            }
            if (!mapVslvoy.containsKey(blnoDo.getId())) {
                MultiBlnoLocalCostResultVO multiBlnoLocalCostResultVo = MultiBlnoLocalCostResultVO.builder()
                        .result(i18nsUtil.getMessage(MultiLocalCostErrorCodeConstants.MULTI_LOCAL_COST_BLNO_VSLVOY_NOTEXIST.getCode(), LocaleContextHolder.getLocale()))
                        .type(calcuteType)
                        .blno(blnoDo.getBlNo()).build();
                calcuteResult.add(multiBlnoLocalCostResultVo);
                continue;
            }
            BlnoVslvoyDO vslvoyDo = mapVslvoy.get(blnoDo.getId()).get(0);
            List<BlnoCntrDO> blnoCntrDos = cntrMap.get(blnoDo.getId());
            BlnoCargoDO blnoCargoDo = cargoMap.get(blnoDo.getId()).get(0);
            if (!agreementResult.containsKey(blnoDo.getBlNo())) {
                MultiBlnoLocalCostResultVO multiBlnoLocalCostResultVo = MultiBlnoLocalCostResultVO.builder()
                        .result(i18nsUtil.getMessage(MultiLocalCostErrorCodeConstants.MULTI_LOCAL_COST_AGRGREMENT_BLNO_COUNT.getCode(), LocaleContextHolder.getLocale()))
                        .type(calcuteType)
                        .blno(blnoDo.getBlNo()).build();
                calcuteResult.add(multiBlnoLocalCostResultVo);
                continue;
            }
            List<DocumentAgreementRateInfoRespDTO> agreementRateInfoResp = agreementResult.get(blnoDo.getBlNo());
            for (DocumentAgreementRateInfoRespDTO rateInfodto : agreementRateInfoResp) {
                ArrayList<AgreementRateInfoDTO> clearFee = rateInfodto.getAgreementRateInfoMap().get(MultiLocalCostCalculationEnum.getAgreementValueInfo(calcuteType).getAgreementValue());
                if (CollectionUtils.isEmpty(clearFee)) {
                    MultiBlnoLocalCostResultVO multiBlnoLocalCostResultVo = MultiBlnoLocalCostResultVO.builder()
                            .result(i18nsUtil.getMessage(MultiLocalCostErrorCodeConstants.MULTI_LOCAL_COST_AGRGREMENT_BLNO_NOT_FOUND.getCode(), LocaleContextHolder.getLocale()))
                            .type(calcuteType)
                            .blno(blnoDo.getBlNo()).build();
                    calcuteResult.add(multiBlnoLocalCostResultVo);
                    continue;
                }
                for (AgreementRateInfoDTO rate : clearFee) {
                    ArrayList<RateInfoDTO> rateInfoList = rate.getRateInfoList();
                    if (CollectionUtils.isEmpty(rateInfoList)) {
                        MultiBlnoLocalCostResultVO multiBlnoLocalCostResultVo = MultiBlnoLocalCostResultVO.builder()
                                .result(i18nsUtil.getMessage(MultiLocalCostErrorCodeConstants.MULTI_LOCAL_COST_AGRGREMENT_NOT_FOUND.getCode(), LocaleContextHolder.getLocale()))
                                .type(calcuteType)
                                .blno(blnoDo.getBlNo()).build();
                        calcuteResult.add(multiBlnoLocalCostResultVo);
                        continue;
                    }
                    for (RateInfoDTO rateInfoDto : rateInfoList) {
                        ChargeUnitCodeEnum chargeUnitCodeEnum = ChargeUnitCodeEnum.valueOf(rateInfoDto.getChargeUnitCode());
                        List<MultiLocalCostDetailDO> billMultiLocalCostDetaiResult = new ArrayList<>(128);
                        List<MultiLocalCostDetailDO> unitMultiLocalCostDetaiResult = new ArrayList<>(256);
                        //按票计算
                        if (chargeUnitCodeEnum == ChargeUnitCodeEnum.BILL) {
                            billMultiLocalCostDetaiResult = this.billCalcute(blnoDo, rateInfoDto, blnoCargoDo, vslvoyDo, imExtype, calcuteType);
                        } else {
                            //按箱计算
                            unitMultiLocalCostDetaiResult = this.unitCalcute(blnoDo, rateInfoDto, blnoCargoDo, vslvoyDo, blnoCntrDos, imExtype, calcuteType);
                        }
                        //处理按箱和按票计算结果
                        MultiBlnoLocalCostResultVO multiBlnoLocalCostResultVo = new MultiBlnoLocalCostResultVO();
                        multiBlnoLocalCostResultVo.setType(calcuteType);
                        multiBlnoLocalCostResultVo.setBlno(blnoDo.getBlNo());
                        if (CollectionUtils.isEmpty(billMultiLocalCostDetaiResult) && CollectionUtils.isEmpty(unitMultiLocalCostDetaiResult)) {
                            multiBlnoLocalCostResultVo.setResult(CommonConstants.ERROR);
                            calcuteResult.add(multiBlnoLocalCostResultVo);
                        } else {
                            multiBlnoLocalCostResultVo.setResult(CommonConstants.RESPONSE_SUCCESS);
                            calcuteResult.add(multiBlnoLocalCostResultVo);
                        }
                        if (CollectionUtils.isNotEmpty(billMultiLocalCostDetaiResult)) {
                            multiLocalCostDetailDoList.addAll(billMultiLocalCostDetaiResult);
                        }
                        if (CollectionUtils.isEmpty(unitMultiLocalCostDetaiResult)) {
                            multiLocalCostDetailDoList.addAll(unitMultiLocalCostDetaiResult);
                        }
                    }
                }
            }
        }
        return multiLocalCostDetailDoList;
    }

    public List<MultiLocalCostDetailDO> unitCalcute(BlnoDO blnoDo,
                                                    RateInfoDTO rateInfoDto,
                                                    BlnoCargoDO blnoCargoDo,
                                                    BlnoVslvoyDO blnoVslvoyDo,
                                                    List<BlnoCntrDO> blnoCntrDos,
                                                    String imExtype,
                                                    String calcuteType) {
        MultiLocalCostCalculationEnum multiLocalCostCalculationEnum = MultiLocalCostCalculationEnum.valueOf(calcuteType);
        List<MultiLocalCostDetailDO> multiLocalCostDetailDoList = new ArrayList<>(256);
        switch (multiLocalCostCalculationEnum) {
            //报关费
            case CUSTOMSCLEARANCEFEE:
                ArrayList<DeclareVersionRateRespDocumentDTO> declareVersionRateList = rateInfoDto.getDeclareVersionRateList();
                for (BlnoCntrDO cntr : blnoCntrDos) {
                    for (DeclareVersionRateRespDocumentDTO rate : declareVersionRateList) {
                        boolean claerFlag = StringUtils.equals(cntr.getCntrType(), rate.getContainerType()) && StringUtils.equals(cntr.getCntrSize(), rate.getContainerSize());
                        if (claerFlag) {
                            multiLocalCostDetailDoList.add(builtCustomerClearDetailDo(rate, blnoDo, blnoCargoDo, cntr, blnoVslvoyDo, imExtype, false, calcuteType));
                        }

                    }
                }
                break;
            //支线费
            case FEEDERCHARGE:
                ArrayList<BargeVersionRateRespDocumentDTO> bargeVersionRateList = rateInfoDto.getBargeVersionRateList();
                for (BlnoCntrDO cntr : blnoCntrDos) {
                    for (BargeVersionRateRespDocumentDTO rate : bargeVersionRateList) {
                        boolean bargeFlag = StringUtils.equals(cntr.getCntrType(), rate.getContainerType()) && StringUtils.equals(cntr.getCntrSize(), rate.getContainerSize());
                        if (bargeFlag) {
                            multiLocalCostDetailDoList.add(builtBargeDetailDo(rate, blnoDo, blnoCargoDo, cntr, blnoVslvoyDo, imExtype, false, calcuteType));
                        }
                    }
                }
                break;
            //装卸费
            case LOADINGUNLOADINGCHARGES:
                ArrayList<TerminalVersionRateBargeRespDocumentDTO> terminalVersionRateList = rateInfoDto.getTerminalVersionRateList();
                for (BlnoCntrDO cntr : blnoCntrDos) {
                    for (TerminalVersionRateBargeRespDocumentDTO rate : terminalVersionRateList) {
                        boolean loadFlag = StringUtils.equals(cntr.getCntrType(), rate.getContainerType()) && StringUtils.equals(cntr.getCntrSize(), rate.getContainerSize());
                        if (loadFlag) {
                            multiLocalCostDetailDoList.add(builtLoadDetailDo(rate, blnoDo, blnoCargoDo, cntr, blnoVslvoyDo, imExtype, false, calcuteType));
                        }
                    }
                }
                break;
            default:
        }
        return multiLocalCostDetailDoList;
    }

    /**
     * 按票计算
     *
     * @param blnoDo       提单信息
     * @param rateInfoDto  协议费率
     * @param blnoCargoDo  货物信息
     * @param blnoVslvoyDo 船名航次
     * @param imExtype     进出口IM EX
     * @param calcuteType  费用大类 报关费、装卸费、直线费
     * @return
     */
    public List<MultiLocalCostDetailDO> billCalcute(BlnoDO blnoDo,
                                                    RateInfoDTO rateInfoDto,
                                                    BlnoCargoDO blnoCargoDo,
                                                    BlnoVslvoyDO blnoVslvoyDo,
                                                    String imExtype,
                                                    String calcuteType) {

        MultiLocalCostCalculationEnum multiLocalCostCalculationEnum = MultiLocalCostCalculationEnum.valueOf(calcuteType);
        List<MultiLocalCostDetailDO> multiLocalCostDetailDoList = new ArrayList<>(256);
        switch (multiLocalCostCalculationEnum) {
            //报关费
            case CUSTOMSCLEARANCEFEE:
                ArrayList<DeclareVersionRateRespDocumentDTO> declareVersionRateList = rateInfoDto.getDeclareVersionRateList();
                for (DeclareVersionRateRespDocumentDTO rate : declareVersionRateList) {
                    multiLocalCostDetailDoList.add(builtCustomerClearDetailDo(rate, blnoDo, blnoCargoDo, null, blnoVslvoyDo, imExtype, true, calcuteType));
                }
                break;
            //支线费
            case FEEDERCHARGE:
                ArrayList<BargeVersionRateRespDocumentDTO> bargeVersionRateList = rateInfoDto.getBargeVersionRateList();
                for (BargeVersionRateRespDocumentDTO rate : bargeVersionRateList) {
                    multiLocalCostDetailDoList.add(builtBargeDetailDo(rate, blnoDo, blnoCargoDo, null, blnoVslvoyDo, imExtype, true, calcuteType));
                }
                break;
            //装卸费
            case LOADINGUNLOADINGCHARGES:
                ArrayList<TerminalVersionRateBargeRespDocumentDTO> terminalVersionRateList = rateInfoDto.getTerminalVersionRateList();
                for (TerminalVersionRateBargeRespDocumentDTO rate : terminalVersionRateList) {
                    multiLocalCostDetailDoList.add(builtLoadDetailDo(rate, blnoDo, blnoCargoDo, null, blnoVslvoyDo, imExtype, true, calcuteType));
                }
                break;
            default:
        }
        return multiLocalCostDetailDoList;
    }

    /**
     * 装卸费明细组装
     *
     * @param loadRate
     * @param blnoDo
     * @param blnoCargoDo
     * @param blnoCntrDo
     * @param blnoVslvoyDo
     * @param imExtype
     * @param billFlag
     * @return
     */
    public MultiLocalCostDetailDO builtLoadDetailDo(TerminalVersionRateBargeRespDocumentDTO loadRate,
                                                    BlnoDO blnoDo,
                                                    BlnoCargoDO blnoCargoDo,
                                                    BlnoCntrDO blnoCntrDo,
                                                    BlnoVslvoyDO blnoVslvoyDo,
                                                    String imExtype,
                                                    Boolean billFlag,
                                                    String calcuteType) {

        MultiLocalCostDetailDO multiLocalCostDetailDo = new MultiLocalCostDetailDO();
        if (ExImTypeEnum.EX.getValue().equals(imExtype)) {
            multiLocalCostDetailDo.setLocalPort(blnoVslvoyDo.getPodCode());
            multiLocalCostDetailDo.setPayment(PaymentEnum.P.getValue());
            multiLocalCostDetailDo.setVoyageCode(blnoVslvoyDo.getVoyageEx());
        } else {
            multiLocalCostDetailDo.setLocalPort(blnoVslvoyDo.getPolCode());
            multiLocalCostDetailDo.setPayment(PaymentEnum.C.getValue());
            multiLocalCostDetailDo.setVoyageCode(blnoVslvoyDo.getVoyageIm());
        }
        if (!billFlag) {
            multiLocalCostDetailDo.setCntrSize(blnoCntrDo.getCntrSize());
            multiLocalCostDetailDo.setCntrType(blnoCntrDo.getCntrType());
            multiLocalCostDetailDo.setContainerNo(blnoCntrDo.getCntrNo());
        }
        multiLocalCostDetailDo.setCalcuteType(calcuteType);
        multiLocalCostDetailDo.setVesselCode(blnoVslvoyDo.getVesselCode());
        multiLocalCostDetailDo.setCargoType(blnoCargoDo.getCargoType());
        multiLocalCostDetailDo.setBlnoId(blnoDo.getId());
        multiLocalCostDetailDo.setChargeCode(loadRate.getItemCode());
        multiLocalCostDetailDo.setCurrency(loadRate.getCurrency());
        multiLocalCostDetailDo.setImExType(imExtype);
        multiLocalCostDetailDo.setPer(billFlag ? ChargeUnitCodeEnum.BILL.getDocValue() : ChargeUnitCodeEnum.UNIT.getDocValue());
        multiLocalCostDetailDo.setQuantity(new BigDecimal(1));
        multiLocalCostDetailDo.setRate(loadRate.getPrice());
        multiLocalCostDetailDo.setAmount(loadRate.getPrice());
        multiLocalCostDetailDo.setSupplier(loadRate.getReceiveCompany());
        return multiLocalCostDetailDo;
    }

    /**
     * 支线费费用组装
     *
     * @param bargeRate    协议返回的费率信息
     * @param blnoDo       提单信息
     * @param blnoCargoDo  货物信息
     * @param blnoCntrDo   箱信息
     * @param blnoVslvoyDo 船名航次信息
     * @param imExtype     进出口信息
     * @param billFlag     是否按票计算 true按票，false按箱
     * @return
     */
    public MultiLocalCostDetailDO builtBargeDetailDo(BargeVersionRateRespDocumentDTO bargeRate,
                                                     BlnoDO blnoDo,
                                                     BlnoCargoDO blnoCargoDo,
                                                     BlnoCntrDO blnoCntrDo,
                                                     BlnoVslvoyDO blnoVslvoyDo,
                                                     String imExtype,
                                                     Boolean billFlag,
                                                     String calcuteType) {

        MultiLocalCostDetailDO multiLocalCostDetailDo = new MultiLocalCostDetailDO();
        if (ExImTypeEnum.EX.getValue().equals(imExtype)) {
            multiLocalCostDetailDo.setLocalPort(blnoVslvoyDo.getPodCode());
            multiLocalCostDetailDo.setPayment(PaymentEnum.P.getValue());
            multiLocalCostDetailDo.setVoyageCode(blnoVslvoyDo.getVoyageEx());
        } else {
            multiLocalCostDetailDo.setLocalPort(blnoVslvoyDo.getPolCode());
            multiLocalCostDetailDo.setPayment(PaymentEnum.C.getValue());
            multiLocalCostDetailDo.setVoyageCode(blnoVslvoyDo.getVoyageIm());
        }
        if (!billFlag) {
            multiLocalCostDetailDo.setCntrSize(blnoCntrDo.getCntrSize());
            multiLocalCostDetailDo.setCntrType(blnoCntrDo.getCntrType());
            multiLocalCostDetailDo.setContainerNo(blnoCntrDo.getCntrNo());
        }
        multiLocalCostDetailDo.setCalcuteType(calcuteType);
        multiLocalCostDetailDo.setVesselCode(blnoVslvoyDo.getVesselCode());
        multiLocalCostDetailDo.setCargoType(blnoCargoDo.getCargoType());
        multiLocalCostDetailDo.setBlnoId(blnoDo.getId());
        multiLocalCostDetailDo.setChargeCode(bargeRate.getItemCode());
        multiLocalCostDetailDo.setCurrency(bargeRate.getCurrency());
        multiLocalCostDetailDo.setImExType(imExtype);
        multiLocalCostDetailDo.setPer(billFlag ? ChargeUnitCodeEnum.BILL.getDocValue() : ChargeUnitCodeEnum.UNIT.getDocValue());
        multiLocalCostDetailDo.setQuantity(new BigDecimal(1));
        multiLocalCostDetailDo.setRate(bargeRate.getPrice());
        multiLocalCostDetailDo.setAmount(bargeRate.getPrice());
        multiLocalCostDetailDo.setSupplier(bargeRate.getSettleCompany());
        return multiLocalCostDetailDo;
    }

    /**
     * 报关费组装详细信息
     *
     * @param rate         协议返回的费率信息
     * @param blnoDo       提单信息
     * @param blnoCargoDo  货物信息
     * @param blnoCntrDo   箱信息
     * @param blnoVslvoyDo 船名航次信息
     * @param imExtype     进出口信息
     * @param billFlag     是否按票计算 true按票，false按箱
     * @return 详细信息
     */
    public MultiLocalCostDetailDO builtCustomerClearDetailDo(DeclareVersionRateRespDocumentDTO rate,
                                                             BlnoDO blnoDo,
                                                             BlnoCargoDO blnoCargoDo,
                                                             BlnoCntrDO blnoCntrDo,
                                                             BlnoVslvoyDO blnoVslvoyDo,
                                                             String imExtype,
                                                             Boolean billFlag,
                                                             String calcuteType) {
        MultiLocalCostDetailDO multiLocalCostDetailDo = new MultiLocalCostDetailDO();
        if (ExImTypeEnum.EX.getValue().equals(imExtype)) {
            multiLocalCostDetailDo.setLocalPort(blnoVslvoyDo.getPodCode());
            multiLocalCostDetailDo.setPayment(PaymentEnum.P.getValue());
            multiLocalCostDetailDo.setVoyageCode(blnoVslvoyDo.getVoyageEx());
        } else {
            multiLocalCostDetailDo.setLocalPort(blnoVslvoyDo.getPolCode());
            multiLocalCostDetailDo.setPayment(PaymentEnum.C.getValue());
            multiLocalCostDetailDo.setVoyageCode(blnoVslvoyDo.getVoyageIm());
        }
        if (!billFlag) {
            multiLocalCostDetailDo.setCntrSize(blnoCntrDo.getCntrSize());
            multiLocalCostDetailDo.setCntrType(blnoCntrDo.getCntrType());
            multiLocalCostDetailDo.setContainerNo(blnoCntrDo.getCntrNo());
        }
        multiLocalCostDetailDo.setCalcuteType(calcuteType);
        multiLocalCostDetailDo.setVesselCode(blnoVslvoyDo.getVesselCode());
        multiLocalCostDetailDo.setCargoType(blnoCargoDo.getCargoType());
        multiLocalCostDetailDo.setBlnoId(blnoDo.getId());
        multiLocalCostDetailDo.setChargeCode(rate.getItemCode());
        multiLocalCostDetailDo.setCurrency(rate.getCurrency());
        multiLocalCostDetailDo.setImExType(imExtype);
        multiLocalCostDetailDo.setPer(billFlag ? ChargeUnitCodeEnum.BILL.getDocValue() : ChargeUnitCodeEnum.UNIT.getDocValue());
        multiLocalCostDetailDo.setQuantity(new BigDecimal(1));
        multiLocalCostDetailDo.setRate(rate.getPrice());
        multiLocalCostDetailDo.setAmount(rate.getPrice());
        multiLocalCostDetailDo.setSupplier(rate.getReceiveCompany());
        return multiLocalCostDetailDo;
    }

    /**
     * 获取协议的费率以及校验协议返回的结果并将结果保存到日志表
     *
     * @param type     计算方式
     * @param blnos    提单信息
     * @param cntrMap  箱信息
     * @param ids      提单id
     * @param imExType 进出口
     */
    public ArrayList<DocumentAgreementRateInfoRespDTO> dealMultiRateData(String type,
                                                                         List<BlnoDO> blnos,
                                                                         Map<Long, List<BlnoCntrDO>> cntrMap,
                                                                         List<Long> ids,
                                                                         String imExType,
                                                                         Map<Long, List<BlnoCargoDO>> cargoMap,
                                                                         List<BlnoVslvoyDO> vslvoyList) {
        //为了避免重复调用航线船期的接口这里先做去重 pol pod transmode voyCodeIm voyCodeEx
        Map<VslvoyGroupByKeyDto, List<BlnoVslvoyDO>> groupMap = vslvoyList.stream().collect(Collectors.groupingBy(item -> new VslvoyGroupByKeyDto(item.getPolCode(), item.getPodCode(), item.getVoyageIm(), item.getVoyageEx(), item.getTransmode())));
        //获取atu时间，使用驳船的船名航次、pol、pod信息查询atu时间
        Map<VslvoyGroupByKeyDto, AtuAndTerminalCodeDto> map = getAtuInfo(groupMap, imExType);
        Map<Long, List<BlnoVslvoyDO>> vslvoyMap = vslvoyList.stream().collect(Collectors.groupingBy(BlnoVslvoyDO::getBlnoId));
        return buildMultiReqRateInfo(type, blnos, cntrMap, cargoMap, imExType, map, vslvoyMap, ids);

    }

    /**
     * 构建获取多联成本费率入参、获取费率、保存日志、校验返回结果
     *
     * @param type
     * @param blnos
     * @param cntrMap
     * @param cargoMap
     * @param imExType
     * @param map
     * @param vslvoyMap
     * @param ids
     */
    public ArrayList<DocumentAgreementRateInfoRespDTO> buildMultiReqRateInfo(String type, List<BlnoDO> blnos, Map<Long, List<BlnoCntrDO>> cntrMap, Map<Long, List<BlnoCargoDO>> cargoMap, String imExType, Map<VslvoyGroupByKeyDto, AtuAndTerminalCodeDto> map, Map<Long, List<BlnoVslvoyDO>> vslvoyMap, List<Long> ids) {
        ArrayList<DocumentAgreementRateInfoReqDTO> dtos = new ArrayList<>();
        List<MultiOpDO> multiOpDos = getMultiOpinfo(ids, imExType);
        String calType = CalculateTypeEnum.MULTI_CALCULATE_LOCAL_COST_EX.getValue();
        for (BlnoDO blnoDo : blnos) {
            DocumentAgreementRateInfoReqDTO rateInfoReqDto = new DocumentAgreementRateInfoReqDTO();
            ArrayList<ContainerInfoDTO> containerInfoList = new ArrayList<>(256);
            BlnoVslvoyDO blnoVslvoyDo = vslvoyMap.get(blnoDo.getId()).get(0);
            BlnoCargoDO blnoCargoDo = cargoMap.get(blnoDo.getId()).get(0);
            AtuAndTerminalCodeDto atuAndTerminalCodeDto = new AtuAndTerminalCodeDto();
            for (VslvoyGroupByKeyDto vslvoyGroupByKeyDto : map.keySet()) {
                atuAndTerminalCodeDto = map.get(vslvoyGroupByKeyDto);
                Boolean flag = StringUtils.equals(vslvoyGroupByKeyDto.getPolCode(), blnoVslvoyDo.getPolCode()) && StringUtils.equals(vslvoyGroupByKeyDto.getPodCode(), blnoVslvoyDo.getPodCode()) && StringUtils.equals(vslvoyGroupByKeyDto.getTransmode(), blnoVslvoyDo.getTransmode()) && StringUtils.equals(vslvoyGroupByKeyDto.getVoyCodeIm(), blnoVslvoyDo.getVoyageIm()) && StringUtils.equals(vslvoyGroupByKeyDto.getVoyCodeEx(), blnoVslvoyDo.getVoyageEx());
                if (flag) {
                    break;
                }
            }
            List<BlnoCntrDO> cntrs = cntrMap.get(blnoDo.getId());
            for (BlnoCntrDO blnoCntrDo : cntrs) {
                ContainerInfoDTO containerInfoDto = new ContainerInfoDTO();
                containerInfoDto.setContainerStatus(blnoDo.getCntrStatus());
                containerInfoDto.setContainerTypeCode(blnoCntrDo.getCntrType());
                containerInfoDto.setCargoTypeCode(blnoCargoDo.getCargoType());
                containerInfoDto.setOverLimitType(oogUtil.buildOog(blnoCntrDo));
                containerInfoList.add(containerInfoDto);
            }
            rateInfoReqDto.setAtu(atuAndTerminalCodeDto.getAtu());
            rateInfoReqDto.setPod(blnoVslvoyDo.getPodCode());
            rateInfoReqDto.setPol(blnoVslvoyDo.getPolCode());
            rateInfoReqDto.setTerminalCode(atuAndTerminalCodeDto.getTerminalCode());
            rateInfoReqDto.setBlNo(blnoDo.getBlNo());
            rateInfoReqDto.setEntityCode(blnoDo.getBlEntity());
            rateInfoReqDto.setTransportType(TransmodeEnum.getBargeTypeByValue(blnoVslvoyDo.getTransmode()));
            if (ExImTypeEnum.EX.getValue().equals(imExType)) {
                rateInfoReqDto.setPortCode(blnoVslvoyDo.getPolCode());
            } else {
                rateInfoReqDto.setPortCode(blnoVslvoyDo.getPodCode());
                calType = CalculateTypeEnum.MULTI_CALCULATE_LOCAL_COST_IM.getValue();
            }
            MultiLocalCostCalculationEnum multiLocalCostCalculationEnum = MultiLocalCostCalculationEnum.valueOf(type);
            HashMap<String, String> agtCsmCode = new HashMap<>(4);
            switch (multiLocalCostCalculationEnum) {
                //报关费
                case CUSTOMSCLEARANCEFEE:
                    agtCsmCode.put(DECLARE, "");
                    rateInfoReqDto.setAgtNumber(blnoDo.getAgreementNo());
                    break;
                //支线费
                case FEEDERCHARGE:
                    agtCsmCode.put(BARGE, "");
                    rateInfoReqDto.setAgtNumber(blnoDo.getAgreementNo());
                    break;
                //装卸费
                case LOADINGUNLOADINGCHARGES:
                    agtCsmCode.put(TERMINAL, "");
                    rateInfoReqDto.setAgtNumber(blnoDo.getAgreementNo());
                    break;
                default:
                    agtCsmCode.put(DECLARE, "");
                    agtCsmCode.put(BARGE, "");
                    agtCsmCode.put(TERMINAL, "");
            }
            rateInfoReqDto.setAgtCsmCodeMap(agtCsmCode);
            rateInfoReqDto.setContainerInfoList(containerInfoList);
            dtos.add(rateInfoReqDto);
        }
        CommonResult<ArrayList<DocumentAgreementRateInfoRespDTO>> agreementRateData = new CommonResult<>();
        ArrayList<DocumentAgreementRateInfoRespDTO> agreementRateResult = new ArrayList<>(128);
        String optLogStatus = CalculateStatusEnum.SUCCESS.getValue();
        Map<String, List<BlnoDO>> blnoMap = blnos.stream().collect(Collectors.groupingBy(BlnoDO::getBlNo));
        String failMsg = "";
        try {
            agreementRateData = agreementService.getAgreementRateData(dtos);
            if (agreementRateData.getCode() == 0) {
                agreementRateResult = agreementRateData.getData();
            } else {
                optLogStatus = CalculateStatusEnum.EXCEPTION.getValue();
            }
            blnoCalculateOperateService.saveMultiLocalCostLog(blnoMap, dtos, agreementRateResult, cntrMap, calType, optLogStatus, failMsg);
        } catch (Exception e) {
            log.error("请求协议接口异常{0}", e.getMessage());
            optLogStatus = CalculateStatusEnum.EXCEPTION.getValue();
            failMsg = e.getMessage();
            blnoCalculateOperateService.saveMultiLocalCostLog(blnoMap, dtos, agreementRateResult, cntrMap, calType, optLogStatus, failMsg);
            throw ServiceExceptionUtil.exception(MultiLocalCostErrorCodeConstants.MULTI_LOCAL_COST_AGRGREMENT_ERROR);
        }
        return agreementRateResult;
    }

    /**
     * 获取atu时间，使用驳船的船名航次、pol、pod信息查询atu时间,进出口都是取的pol的atu时间
     *
     * @param groupMap
     * @param imExType
     * @return
     */
    public Map<VslvoyGroupByKeyDto, AtuAndTerminalCodeDto> getAtuInfo(Map<VslvoyGroupByKeyDto, List<BlnoVslvoyDO>> groupMap, String imExType) {
        Map<VslvoyGroupByKeyDto, AtuAndTerminalCodeDto> map = new HashMap<>();
        for (VslvoyGroupByKeyDto vslvoyGroupByKeyDtoSet : groupMap.keySet()) {
            BargePolPodReqDTO bargePolPodReqDto = new BargePolPodReqDTO();
            bargePolPodReqDto.setBargeType(TransmodeEnum.getBargeTypeByValue(vslvoyGroupByKeyDtoSet.getTransmode()));
            bargePolPodReqDto.setDateType(DATA_TYPE);
            bargePolPodReqDto.setPol(vslvoyGroupByKeyDtoSet.getPolCode());
            bargePolPodReqDto.setPod(vslvoyGroupByKeyDtoSet.getPodCode());
            if (ExImTypeEnum.EX.getValue().equals(imExType)) {
                bargePolPodReqDto.setPolExVoy(vslvoyGroupByKeyDtoSet.getVoyCodeEx());
            } else {
                bargePolPodReqDto.setPolExVoy(vslvoyGroupByKeyDtoSet.getVoyCodeIm());
            }
            List<SoVslvoyScheduleVO> soVslvoyScheduleVoList = voyScheduleService.multiLineScheduleByPolPod(bargePolPodReqDto);
            if (CollectionUtils.isEmpty(soVslvoyScheduleVoList)) {
                throw ServiceExceptionUtil.exception(MultiLocalCostErrorCodeConstants.MULTI_LOCAL_COST_ATU_EXCEPTION);
            }
            SoVslvoyScheduleVO scheduleVo = soVslvoyScheduleVoList.get(0);
            AtuAndTerminalCodeDto atuAndTerminalCodeDto = new AtuAndTerminalCodeDto();
            atuAndTerminalCodeDto.setAtu(scheduleVo.getPolAtu());
            atuAndTerminalCodeDto.setTerminalCode(scheduleVo.getPolTerminal());
            map.put(vslvoyGroupByKeyDtoSet, atuAndTerminalCodeDto);
        }
        return map;
    }

    /**
     * 查询驳船的船名航次信息
     *
     * @param ids
     * @param imExType
     * @return
     */
    public List<BlnoVslvoyDO> getBlnoVslvoyInfo(List<Long> ids, String imExType) {
        LambdaQueryWrapperX<BlnoVslvoyDO> lambdaQueryWrapper = new LambdaQueryWrapperX<>();
        if (ExImTypeEnum.EX.getValue().equals(imExType)) {
            lambdaQueryWrapper.in(BlnoVslvoyDO::getBlnoId, ids).eq(BlnoVslvoyDO::getRouteNo, 1).ne(BlnoVslvoyDO::getTransmode, TransmodeEnum.LINER.getValue());
        } else {
            lambdaQueryWrapper.in(BlnoVslvoyDO::getBlnoId, ids).gt(BlnoVslvoyDO::getRouteNo, 1).ne(BlnoVslvoyDO::getTransmode, TransmodeEnum.LINER.getValue());
        }
        return blnoVslvoyMapper.selectList(lambdaQueryWrapper);
    }

    /**
     * 获取提单信息
     *
     * @param ids
     * @return
     */
    public List<BlnoDO> getBlnoInfo(List<Long> ids) {
        LambdaQueryWrapperX<BlnoDO> blnoDoLambdaQueryWrapper = new LambdaQueryWrapperX<>();
        blnoDoLambdaQueryWrapper.in(BlnoDO::getId, ids).eq(BlnoDO::getValid, SiValidEnum.VALID.getValue()).in(BlnoDO::getSiStatus, SI_STATUS).eq(BlnoDO::getPodBlFlag, true);
        return blnoMapper.selectList(blnoDoLambdaQueryWrapper);
    }

    /**
     * 获取箱信息 同时处理拼箱问题
     *
     * @param ids
     * @return
     */
    public List<BlnoCntrDO> getBlnoCntrInfo(List<Long> ids) {
        LambdaQueryWrapperX<BlnoCntrDO> blnoDoLambdaQueryWrapper = new LambdaQueryWrapperX<>();
        blnoDoLambdaQueryWrapper.in(BlnoCntrDO::getBlnoId, ids);
        List<BlnoCntrDO> blnoCntrDoList = blnoCntrMapper.selectList(blnoDoLambdaQueryWrapper);


        return blnoCntrMapper.selectList(blnoDoLambdaQueryWrapper);
    }

    /**
     * 获取货类信息
     *
     * @param ids
     * @return
     */
    public List<BlnoCargoDO> getBlnoCargoInfo(List<Long> ids) {
        LambdaQueryWrapperX<BlnoCargoDO> blnoDoLambdaQueryWrapper = new LambdaQueryWrapperX<>();
        blnoDoLambdaQueryWrapper.in(BlnoCargoDO::getBlnoId, ids);
        return blnoCargoMapper.selectList(blnoDoLambdaQueryWrapper);
    }

    /**
     * 获取
     *
     * @param ids
     * @param imExType
     * @return
     */
    public List<MultiOpDO> getMultiOpinfo(List<Long> ids, String imExType) {
        LambdaQueryWrapperX<MultiOpDO> multiOpLambdaQueryWrapper = new LambdaQueryWrapperX<>();
        multiOpLambdaQueryWrapper.in(MultiOpDO::getBlnoId, ids).eq(MultiOpDO::getImExType, imExType);
        if (ExImTypeEnum.EX.getValue().equals(imExType)) {
            multiOpLambdaQueryWrapper.eq(MultiOpDO::getOperationType, MulitOpOperationTypeEnum.CUSTOM_BROKER.getValue());
        } else {
            List<String> operationType = Arrays.asList(MulitOpOperationTypeEnum.CONTRACT_NO.getValue(), MulitOpOperationTypeEnum.CUSTOM_BROKER.getValue(), MulitOpOperationTypeEnum.OPERATION_AGENT.getValue());
            multiOpLambdaQueryWrapper.in(MultiOpDO::getOperationType, operationType);
        }
        return multiOpMapper.selectList(multiOpLambdaQueryWrapper);
    }

    public void deleteMultiLocalCostDetailInfo(List<Long> ids, String calcuteType) {
        LambdaQueryWrapperX<MultiLocalCostDetailDO> lambdaQueryWrapper = new LambdaQueryWrapperX<>();
        lambdaQueryWrapper.in(MultiLocalCostDetailDO::getBlnoId, ids);
        MultiLocalCostCalculationEnum multiLocalCostCalculationEnum = MultiLocalCostCalculationEnum.valueOf(calcuteType);
        switch (multiLocalCostCalculationEnum) {
            //报关费
            case CUSTOMSCLEARANCEFEE:
                //支线费
            case FEEDERCHARGE:
                //装卸费
            case LOADINGUNLOADINGCHARGES:
                lambdaQueryWrapper.eq(MultiLocalCostDetailDO::getCalcuteType, calcuteType);
                break;
            default:
                break;
        }
        multiLocalCostDetailMapper.delete(lambdaQueryWrapper);
    }
}
